﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Data.SqlClient;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace LANmine
{
    public partial class ComposForm : Form
    {
        private Language lmLang;
        private User lmUser;
        private Database roDb = new Database("ro");
        private Database rwDb = new Database("rw");

        /// <summary>
        /// Holds the currently selected game
        /// </summary>
        public ListViewGame SelectedGame { get; set; }

        /// <summary>
        /// Default ComposForm Constructor
        /// </summary>
        public ComposForm()
        {
            InitializeComponent();
        }

        /// <summary>
        /// Custom ComposForm Constructor
        /// </summary>
        /// <param name="lmLang">LANmine Language Object</param>
        /// <param name="lmUser">LANmine User Object</param>
        public ComposForm(Language lmLang, User lmUser) : this()
        {
            /** Save objects */
            this.lmLang = lmLang;
            this.lmUser = lmUser;

            /** No game has been selected yet */
            this.SelectedGame = null;

            /** Dock the window */
            this.Dock = DockStyle.Fill;

            ApplyLanguage();
        }

        #region Language Specifications

        /// <summary>
        /// Overwrite all text properties of all elements in all forms with the right ones.
        /// </summary>
        private void ApplyLanguage()
        {
            // Header
            this.lblCompos.Text = this.lmLang.GetString("menu.btnCompos");
            // Other controls
            this.btnCreateTeam.Text = this.lmLang.GetString("compos.btnCreateTeam");
            this.btnRegister.Text = this.lmLang.GetString("compos.btnRegister");
            this.btnManageTeams.Text = this.lmLang.GetString("compos.btnManageTeams");
        }

        #endregion

        #region Member Functions
        
        /// <summary>
        /// Fetch a list of games from the database, including their metadata.
        /// </summary>
        /// <param name="lvw">ListView to manipulate</param>
        private void LoadGames(ListView lvw)
        {
            /** Define temp variables */
            UInt16 gSize;
            String gName;
            String gSign;

            /** Define database objects */
            DataSet ds = new DataSet();
            DataTable dt;
            SqlConnection dbConn = this.roDb.getDbConn();
            SqlDataAdapter da;

            /** Create DataAdapter */
            da = new SqlDataAdapter("SELECT *, dbo.GetSignups(id) AS signups FROM games", dbConn);

            if (ds.Tables["games"] != null)
                ds.Tables["games"].Rows.Clear();

            /** Fill the dataset with the incoming data and put it into the 'games' table */
            da.Fill(ds, "games");

            /** Extract the table from the DataSet */
            dt = ds.Tables["games"];

            // Clear the ListView
            lvw.Clear();

            /** Add three columns */
            lvw.Columns.AddRange(new ColumnHeader[] { new ColumnHeader(), new ColumnHeader(), new ColumnHeader() });

            /** Add groups with the given names */
            lvw.Groups.Add(new ListViewGroup("solo", lmLang.GetString("compos.lvwGamesSolo")));
            lvw.Groups.Add(new ListViewGroup("team", lmLang.GetString("compos.lvwGamesTeam")));

            foreach (DataRow r in dt.Rows)
            {
                /** Prepare Data */
                gSize = Convert.ToUInt16(r["teamgrootte"].ToString());
                gName = r["naam"].ToString();

                /** Get amount of signups */
                if(gSize > 1)
                    gSign = r["signups"].ToString() + " " + lmLang.GetString("compos.lvwGamesTeamSign");
                else
                    gSign = r["signups"].ToString() + " " + lmLang.GetString("compos.lvwGamesCompSign");

                /** Create ListViewGame based on the database row */
                ListViewGame cItem = new ListViewGame(new string[] { r["naam"].ToString(), gSize + "v" + gSize, gSign });

                /**
                 * Extract data into the object.
                 */
                cItem.ID = Convert.ToUInt32(r["id"].ToString());
                cItem.ShortName = r["short"].ToString();
                cItem.TeamSize = gSize;
                cItem.Name = gName;

                /** Push the current item to the team game group */
                lvw.Items.Add(cItem);

                /** Assign the added game to the correct group */
                if (gSize > 1)
                    lvw.Items[gName].Group = lvwGames.Groups["team"];
                else
                    lvw.Items[gName].Group = lvwGames.Groups["solo"];
            }
        }

        /// <summary>
        /// Load a list of teams for the given game ID into the given ListView.
        /// </summary>
        /// <param name="gId">Game ID to load from</param>
        /// <param name="lvw">ListView to manipulate</param>
        private void LoadTeams(UInt32 gId, ListView lvw)
        {
            /** Define database objects */
            DataSet ds = new DataSet();
            DataTable dt;
            SqlConnection dbConn = this.roDb.getDbConn();
            SqlDataAdapter da;

            try
            {
                da = new SqlDataAdapter("lmGetGameTeams", dbConn);
                da.SelectCommand.CommandType = CommandType.StoredProcedure;

                /** Add Parameters */
                da.SelectCommand.Parameters.Add("@GameId", SqlDbType.BigInt, 0);
                da.SelectCommand.Parameters["@GameId"].Value = gId;

                /** Fill the DataSet from the DataAdapter */
                da.Fill(ds, "teams");

                /** Extract the teams table from the dataset */
                dt = ds.Tables["teams"];

                /** Clear the ListView */
                lvw.Clear();

                /** Add two columns to the ListView */
                lvw.Columns.AddRange(new ColumnHeader[] { new ColumnHeader(), new ColumnHeader() });

                /** Add a group to clarify the meaning of the ListView */
                ListViewGroup sGroup = new ListViewGroup("team", this.SelectedGame.Name + " - " + lmLang.GetString("compos.lvwTeamsTeamsTitle") + dt.Rows.Count);
                lvw.Groups.Add(sGroup);

                /** Check for empty result set */
                if (dt.Rows.Count > 0)
                {
                    /** Loop through a list of teams */
                    foreach (DataRow r in dt.Rows)
                    {
                        /** Add the current record to the ListView */
                        lvw.Items.Add(new ListViewItem(new String[] { r["naam"].ToString() + " [" + r["tag"].ToString() + "]", lmLang.GetString("compos.miscCaptain") + ": " + r["gebruikersnaam"].ToString() }, sGroup));
                    }
                }
                else
                {
                    /** Add an empty record */
                    lvw.Items.Add(new ListViewItem(lmLang.GetString("compos.errNoTeams"), sGroup));
                }
            }
            catch (Exception e)
            {
                MessageBox.Show(lmLang.GetString("compos.errTeamList") + "\n" + e.Message);
            }
        }

        /// <summary>
        /// Load a list of competitors for the given game ID into the given ListView.
        /// </summary>
        /// <param name="gId">Game ID to load from</param>
        /// <param name="lvw">ListView to manipulate</param>
        private void LoadCompetitors(UInt32 gId, ListView lvw)
        {
            /** Define database objects */
            DataSet ds = new DataSet();
            DataTable dt;
            SqlConnection dbConn = this.roDb.getDbConn();
            SqlDataAdapter da;

            try
            {
                /** Create SqlDataAdapter */
                da = new SqlDataAdapter("lmGetGameCompetitors", dbConn);
                da.SelectCommand.CommandType = CommandType.StoredProcedure;

                /** Add Parameters */
                da.SelectCommand.Parameters.Add("@GameId", SqlDbType.BigInt, 0, "id");
                da.SelectCommand.Parameters["@GameId"].Value = gId;

                /** Fill the DataSet from the DataAdapter */
                da.Fill(ds, "comps");

                /** Extract the teams table from the dataset */
                dt = ds.Tables["comps"];

                /** Clear the ListView */
                lvw.Clear();

                /** Add two columns to the ListView */
                lvw.Columns.AddRange(new ColumnHeader[] { new ColumnHeader(), new ColumnHeader() });

                /** Add a group to clarify the meaning of the ListView */
                ListViewGroup sGroup = new ListViewGroup("solo", lmLang.GetString("compos.lvwTeamsCompsTitle") + dt.Rows.Count);
                lvw.Groups.Add(sGroup);

                if(dt.Rows.Count > 0)
                {
                    /** Loop through a list of teams */
                    foreach (DataRow r in dt.Rows)
                    {
                        String u = r["gebruikersnaam"].ToString();
                        String f = r["voornaam"].ToString();
                        String l = r["naam"].ToString();

                        /** Add the current record to the ListView */
                        lvw.Items.Add(new ListViewItem(new String[] { u, f + " " + l}, sGroup));
                    }
                }
                else
                {
                    /** Add a blank record to the ListView */
                    lvw.Items.Add(new ListViewItem(lmLang.GetString("compos.errNoComps"), sGroup));
                }
            }
            catch (Exception e)
            {
                /** Error getting a list of competitors */
                MessageBox.Show(lmLang.GetString("compos.errCompList") + "\n" + e.Message);
            }
        }

        /// <summary>
        /// Take a game ID and a user ID and check if the user has signed up for a specific game.
        /// </summary>
        /// <param name="gId">Game ID to check for</param>
        /// <returns>Operation success</returns>
        private bool CheckSignup(UInt32 gId)
        {
            /** Get SQL connection */
            SqlConnection dbConn = this.roDb.getDbConn();

            /** Return value */
            bool rVal;
            
            /** Query */
            SqlCommand cQry = new SqlCommand("lmCheckSignup", dbConn);
            cQry.CommandType = CommandType.StoredProcedure;

            /** Add Parameters */
            cQry.Parameters.Add("@GameId", SqlDbType.BigInt, 0);
            cQry.Parameters.Add("@UserId", SqlDbType.BigInt, 0);

            /* Set the parameters' value */
            cQry.Parameters["@GameId"].Value = gId;
            cQry.Parameters["@UserId"].Value = lmUser.ID;

            try
            {
                /** Open connection */
                dbConn.Open();
                /** Get DB reader */
                SqlDataReader dbRead = cQry.ExecuteReader();

                /** Check for results */
                if (dbRead.HasRows)
                    rVal = true;
                else
                    rVal = false;

                /** Close connection */
                dbConn.Close();

            }
            catch (Exception e)
            {
                /** Close connection */
                dbConn.Close();
                MessageBox.Show(lmLang.GetString("compos.errGetSignup") + "\n" + e.Message);
                return false;
            }

            /** Return the result */
            return rVal;
        }

        /// <summary>
        /// Sign up the current user for the specified solo game.
        /// </summary>
        /// <param name="gId">Game ID to look up from</param>
        private void SoloSignup(UInt32 gId)
        {
            /** Declare database objects */
            DataSet ds = new DataSet();
            SqlDataAdapter da;
            DataRow newSolo;
            SqlCommandBuilder cb;

            /** Build query string */
            String sQry = "SELECT * FROM compos_solo_inschrijvingen";

            try
            {
                /** Create DataAdapter¨*/
                da = new SqlDataAdapter(sQry, rwDb.getDbConn());
                cb = new SqlCommandBuilder(da);

                /** Get the schema from the Teams table */
                da.FillSchema(ds, SchemaType.Source, "signups");

                /** Create and fill new row */
                newSolo = ds.Tables["signups"].NewRow();

                newSolo["deelnemer_id"] = lmUser.ID;
                newSolo["game_id"] = gId;

                /** Push row to DataTable */
                ds.Tables["signups"].Rows.Add(newSolo);

                /** Push DataTable to database */
                da.Update(ds, "signups");
            }
            catch
            {
                throw;
            }
        }

        /// <summary>
        /// Rebinds the Join button's event handler to the solo handler or the team handler.
        /// </summary>
        /// <param name="reTarg">Game type to rebind the join button to</param>
        private void RebindJoin(String reTarg)
        {
            /** Unbind the two possible existing event handlers */
            this.btnRegister.Click -= btnRegister_Click_Team;
            this.btnRegister.Click -= btnRegister_Click_Solo;

            if(reTarg == "solo")
                /** Binds the button to Solo */
                this.btnRegister.Click += btnRegister_Click_Solo;
            else if(reTarg == "team")
                /** Binds the button to Team */
                this.btnRegister.Click += btnRegister_Click_Team;
        }

        #endregion

        #region Event Handlers

        private void ComposForm_ClientSizeChanged(object sender, EventArgs e)
        {
            /** Resize ListView tiles */
            lvwGames.TileSize = new Size(lvwGames.ClientSize.Width, lvwGames.TileSize.Height);
            lvwTeams.TileSize = new Size(lvwTeams.ClientSize.Width/3, lvwTeams.TileSize.Height);
        }

        private void ComposForm_Activated(object sender, EventArgs e)
        {
            try
            {
                /** Load a list of games */
                LoadGames(this.lvwGames);
                /** Add an 'empty' record */
                this.lvwTeams.Items.Add(lmLang.GetString("compos.lvwTeamsSelect"));
                // Select the first item
                lvwGames.Items[0].Selected = true;
            }
            catch (Exception ex)
            {
                MessageBox.Show(lmLang.GetString("compos.errGameList") + "\n" + ex.ToString());
            }
        }

        private void lvwGames_SelectedIndexChanged(object sender, EventArgs e)
        {
            //MessageBox.Show("SelectedIndexChanged");
            /** Make sure we have an item selected */
            if (lvwGames.SelectedItems.Count > 0 && this.SelectedGame != (ListViewGame)lvwGames.SelectedItems[0])
            {
                /** Save the current selection state */
                this.SelectedGame = (ListViewGame)lvwGames.SelectedItems[0];

                /** Clear the Teams ListView */
                this.lvwTeams.Clear();

                /** Extract row variables */
                String entryGroup = this.SelectedGame.Group.Name;
                UInt32 entryGameId = this.SelectedGame.ID;

                /** Check whether a solo game or a team game has been selected */
                if (entryGroup == "solo")
                {
                    /** Load a list of competitors */
                    LoadCompetitors(entryGameId, this.lvwTeams);

                    // Check if the user's already enrolled in the highlighted team competition
                    if (CheckSignup(entryGameId))
                    {
                        /** Disable the Join button */
                        this.btnRegister.Enabled = false;
                    }
                    else
                    {
                        /** Enable the Join button and bind it to the team join screen */
                        this.btnRegister.Enabled = true;
                    }
                }
                else if (entryGroup == "team")
                {
                    /** Load a list of teams */
                    LoadTeams(entryGameId, this.lvwTeams);

                    /** Check if the user's already enrolled in the highlighted team competition */
                    if (CheckSignup(entryGameId))
                    {
                        /** Disable the Join button */
                        this.btnRegister.Enabled = false;
                    }
                    else
                    {
                        /** Enable the Join button */
                        this.btnRegister.Enabled = true;
                    }
                }

                /** Rebind the Join button */
                RebindJoin(entryGroup);
            }
        }

        void btnRegister_Click_Solo(object sender, EventArgs e)
        {
            try
            {
                /** Sign up for the game */
                SoloSignup(this.SelectedGame.ID);
                /** Disable the button */
                this.btnRegister.Enabled = false;
                // Reload the games list
                LoadGames(this.lvwGames);
                /** Reload the competitors' list */
                LoadCompetitors(this.SelectedGame.ID, this.lvwTeams);
            }
            catch (Exception ex)
            {
                MessageBox.Show(lmLang.GetString("compos.errSignup") + " " + this.SelectedGame.Name + ".\n" + ex.Message);
            }
        }

        void btnRegister_Click_Team(object sender, EventArgs e)
        {
            try
            {
                using (SignupTeam stForm = new SignupTeam(this.SelectedGame, this.lmLang, this.lmUser))
                {
                    DialogResult dRes = stForm.ShowDialog();

                    if(dRes == DialogResult.OK)
                    {
                        LoadGames(this.lvwGames);
                        LoadTeams(this.SelectedGame.ID, lvwTeams);
                    }
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(lmLang.GetString("compos.errSignup") + " " + this.SelectedGame.Name + ".\n" + ex.Message);
            }
        }

        private void btnCreateTeam_Click(object sender, EventArgs e)
        {
            using (CreateTeam ctForm = new CreateTeam(lmLang, lmUser))
            {
                ctForm.ShowDialog();
            }
        }

        private void btnManageTeams_Click(object sender, EventArgs e)
        {
            using (ManageTeams mtForm = new ManageTeams(lmUser, lmLang))
            {
                mtForm.ShowDialog();
            }
        }

        #endregion
    }
}